package com.talkweb.platform.commonapi.utils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.Id;

import org.apache.commons.lang.StringUtils;

import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

/**
 * 实体对象帮助类
 * 
 * @author lzy
 */
public class EntityUtils
{
    private static Map<Class<?>, Method[]> keyReadMethodsMap = new HashMap<Class<?>, Method[]>();
    
    private static Map<Class<?>, Field> keyFieldMap = new HashMap<Class<?>, Field>();
    
    private static Map<Class<?>, Map<String, Method>> fieldGettersMap = new HashMap<Class<?>, Map<String, Method>>();
    
    /**
     * 获取主键值(非联合主键)
     *
     * @param entity
     * @return String [返回类型说明]
     * @see [类、类#方法、类#成员]
     */
    public static String getKey(Object entity)
    {
        if (null == entity)
        {
            return null;
        }
        Method[] methods = getKeyReadMethods(entity.getClass());
        if (null == methods || methods.length != 1)
        {
            return null;
        }
        try
        {
            Object value = methods[0].invoke(entity);
            return null == value ? null : value.toString();
        }
        catch (Exception e)
        {
        }
        return null;
    }
    
    @SuppressWarnings("rawtypes")
    public static Object getFieldValue(String fieldName, Object entity)
    {
        if (StringUtils.isEmpty(fieldName) || null == entity)
        {
            return null;
        }
        
        if (entity instanceof Map)
        {
            return ((Map)entity).get(fieldName);
        }
        
        try
        {
            Map<String, Method> methodMap = fieldGettersMap.get(entity.getClass());
            Method getMethod = null;
            if (null == methodMap)
            {
                methodMap = new HashMap<String, Method>();
                fieldGettersMap.put(entity.getClass(), methodMap);
            }
            if ((getMethod = methodMap.get(fieldName)) == null)
            {
                PropertyDescriptor pd = new PropertyDescriptor(fieldName, entity.getClass());
                getMethod = pd.getReadMethod();
                methodMap.put(fieldName, getMethod);
            }
            return getMethod.invoke(entity);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        
        return null;
    }
    
    /**
     * 获取主键值(以separator分隔)
     *
     * @param entity
     * @param separator
     * @return String [返回类型说明]
     * @see [类、类#方法、类#成员]
     */
    public static String getKey(Object entity, String separator)
    {
        if (null == entity)
        {
            return null;
        }
        Method[] methods = getKeyReadMethods(entity.getClass());
        if (null == methods || methods.length == 0)
        {
            return null;
        }
        try
        {
            String[] keys = new String[methods.length];
            for (int i = 0; i < methods.length; i++)
            {
                Object key = methods[i].invoke(entity);
                keys[i] = String.valueOf(key);
            }
            return StringUtils.join(keys, separator);
        }
        catch (Exception e)
        {
        }
        return null;
    }
    
    /**
     * 获取主键字段名
     *
     * @param clazz
     * @return String [返回类型说明]
     * @see [类、类#方法、类#成员]
     */
    public static String getKeyFieldName(Class<?> clazz)
    {
        Field keyField = keyFieldMap.get(clazz);
        if (null == keyField)
        {
            for (Field field : clazz.getDeclaredFields())
            {
                if (isKeyField(field))
                {
                    keyField = field;
                    keyFieldMap.put(clazz, field);
                    break;
                }
            }
        }
        return null == keyField ? null : keyField.getName();
    }
    
    /**
     * 获取主键(以@Id注解标识)的get方法
     *
     * @param clazz
     * @return Method[] [返回类型说明]
     * @see [类、类#方法、类#成员]
     */
    public static Method[] getKeyReadMethods(Class<?> clazz)
    {
        Method[] methods = keyReadMethodsMap.get(clazz);
        if (null == methods)
        {
            try
            {
                // key字段列表
                List<Field> keyFieldList = new ArrayList<Field>();
                for (Field field : clazz.getDeclaredFields())
                {
                    if (isKeyField(field))
                    {
                        keyFieldList.add(field);
                    }
                }
                if (keyFieldList.isEmpty())
                {
                    return null;
                }
                // 按字段名排序
                Collections.sort(keyFieldList, new Comparator<Field>()
                {
                    public int compare(Field o1, Field o2)
                    {
                        return o1.getName().compareTo(o2.getName());
                    }
                });
                methods = new Method[keyFieldList.size()];
                for (int i = 0; i < keyFieldList.size(); i++)
                {
                    PropertyDescriptor pd = new PropertyDescriptor(keyFieldList.get(i).getName(), clazz);
                    methods[i] = pd.getReadMethod();
                }
                keyReadMethodsMap.put(clazz, methods);
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
        return methods;
    }
    
    /**
     * 判断是否主键字段
     *
     * @param field
     * @return boolean [返回类型说明]
     * @see [类、类#方法、类#成员]
     */
    private static boolean isKeyField(Field field)
    {
        return null != field.getAnnotation(Id.class);
    }
    
    /**
     * 获取一个类的泛型
     *
     * @param clazz
     * @param supperClazz
     * @return Class<?> [返回类型说明]
     * @see [类、类#方法、类#成员]
     */
    public static Class<?> getGenericType(Class<?> clazz, Class<?> supperClazz)
    {
        if (null == clazz || clazz.equals(Object.class))
        {
            return null;
        }
        
        Class<?> gt = null;
        
        Type[] tps = clazz.getGenericInterfaces();
        if (null != tps && tps.length > 0)
        {
            for (Type tp : tps)
            {
                if (tp instanceof ParameterizedTypeImpl && ((ParameterizedTypeImpl)tp).getRawType().equals(supperClazz))
                {
                    ParameterizedTypeImpl pti = (ParameterizedTypeImpl)tp;
                    Type[] args = pti.getActualTypeArguments();
                    if (null != args && args.length > 0)
                    {
                        return (Class<?>)args[0];
                    }
                }
                else if ((gt = getGenericType((Class<?>)tp, supperClazz)) != null)
                {
                    return gt;
                }
            }
        }
        
        Type st = clazz.getGenericSuperclass();
        if (null != st)
        {
            if (st instanceof ParameterizedTypeImpl)
            {
                ParameterizedTypeImpl pti = (ParameterizedTypeImpl)st;
                Type[] args = pti.getActualTypeArguments();
                if (null != args && args.length > 0)
                {
                    return (Class<?>)args[0];
                }
            }
            else if ((gt = getGenericType((Class<?>)st, supperClazz)) != null)
            {
                return gt;
            }
        }
        return gt;
    }
    
}
